
#include "../include/GPCompute.h"
#include "../include/GPDefineDX81.h"

#include <d3d8.h>

typedef struct _GpArray {
    IDirect3DSurface8* pSurface;
    D3DLOCKED_RECT lockinfo;
} DX81Array;

typedef struct _GpVrray {
    IDirect3DTexture8* pTexture;
} DX81Vrray;

typedef struct _GpShapeRect {
    IDirect3DVertexBuffer8* pVB;
    IDirect3DIndexBuffer8*  pIB;
    int rectcount;
    GP_RECT rects[1];
} DX81ShapeRect;

typedef struct _GpCompute {
    IDirect3D8*				pD3D;
    IDirect3DDevice8*		pd3dDevice;
    IDirect3DSurface8*		pRenderTarget;    // current screen pointer save
    IDirect3DSurface8*		pScreenTarget;    // point to the backbuffer fixed 
    IDirect3DSurface8*		pDepthSurface;    // point to the depthbuffer fixed 

    GP_CALCSTATE			currstate;
    int beginidx, endidx;
    UINT numbackbuf;
	D3DFORMAT				BackBufFormat;
	DWORD const*			vertexdecl;
} DX81Impl;

typedef struct _GpComputeSub {
	IDirect3DSwapChain8*	pSwapChain;
	DX81Impl*				pMain;
} DX81Sub;

typedef struct {
    float x, y, z, w;
    float u, v;
} VERTEX;

// global specs : 
DWORD VERTEXflag, VERTEXusage;
DWORD MaxTextureWidth, MaxTextureHeight, MaxPrimitiveCount;
DWORD MaxVertexShaderConst, MaxPixelShaderConst;
static GP_HINT g_hint;
static DWORD const g_dwDecl[] = {
    D3DVSD_STREAM(0), 
    // input register 
    D3DVSD_REG(D3DVSDE_POSITION,        D3DVSDT_FLOAT4),        // input register v0
//  D3DVSD_REG(D3DVSDE_DIFFUSE,         D3DVSDT_D3DCOLOR),      // input register v5
//  D3DVSD_REG(D3DVSDE_SPECULAR,        D3DVSDT_D3DCOLOR),      // input register v6
    D3DVSD_REG(D3DVSDE_TEXCOORD0,       D3DVSDT_FLOAT2),        // input register v7
    D3DVSD_END(), 
};

// --------------------------------------------------------------------------------------------------- //

#include "UtilsDX81.hpp"
inline ULONG Check_Release(const char* desc, ULONG nRef) {
    if ( nRef > 0 ) {
        char strbuf[512];
        wsprintfA( strbuf, "D3DLeak: %s ref=%u\n", desc, nRef );
        OutputDebugStringA( strbuf );
    }
    return nRef;
}
#define CHECK_RELEASE(p)    (Check_Release(#p, p)!=0)

// --------------------------------------------------------------------------------------------------- //

int GpComputePtrByHint( int hint, void* inptr, void **outptr )
{
    switch( hint ) {
        case 0: *outptr = ((HGPGPU)inptr)->pd3dDevice;
            break;
        default: return -1;
    }
    return (0);
}

int GpComputeInit( HGPGPU* hgpgpu, GP_CONTEXT const* ctx, GP_HINT hint )
{
    HWND hwndcanvas = (HWND)ctx->canvas;

	g_hint = hint; // __deprecated ... 

    IDirect3D8* g_pD3D;
    IDirect3DDevice8* g_pd3dDevice;
    IDirect3DSurface8* g_pScreenTarget;
    IDirect3DSurface8* g_pDepthSurface;
    UINT numbackbuf;

    // conversion from GP -> DX81 context: 
    if( NULL == (g_pD3D = Direct3DCreate8( D3D_SDK_VERSION )) )
        return Fail_Direct3DCreate8;

    // vertex and pixel shader check : 
    D3DCAPS8 Caps, *pCaps = &Caps;
    if (FAILED(g_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &Caps)))
        return Fail_GetDeviceCaps;
    // v.s. 
    if (pCaps->VertexShaderVersion < D3DVS_VERSION(1,1)) {
        VERTEXflag = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
        VERTEXusage = D3DUSAGE_SOFTWAREPROCESSING;
    } else {
        VERTEXflag = D3DCREATE_HARDWARE_VERTEXPROCESSING;
        VERTEXusage = 0;
    }

    // p.s. 
    if (pCaps->PixelShaderVersion < D3DPS_VERSION(1,4)) {
        return Fail_PixelShaderVersion;
    }
    if (pCaps->MaxSimultaneousTextures < MAXARRAYCOUNT) {
        return Fail_MaxSimultaneousTextures;
    }
    // other prop : 
    MaxTextureWidth		= pCaps->MaxTextureWidth;
    MaxTextureHeight	= pCaps->MaxTextureHeight;
    MaxPrimitiveCount	= pCaps->MaxPrimitiveCount;
	MaxVertexShaderConst	= pCaps->MaxVertexShaderConst;
	MaxPixelShaderConst		= 8;
/*
    D3DDISPLAYMODE d3ddm;
    if (FAILED( g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm)))
        return Fail_GetAdapterDisplayMode;
*/
    D3DPRESENT_PARAMETERS d3dpp; // settings of parameter : 
    ZeroMemory( &d3dpp, sizeof(d3dpp) );
    d3dpp.BackBufferWidth = ctx->width;
    d3dpp.BackBufferHeight = ctx->height;
    d3dpp.BackBufferFormat = MappingFormat( ctx->colortype, ctx->colorprecision );
    d3dpp.BackBufferCount = 3; // as much as possible !! 

    d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;

    d3dpp.SwapEffect = D3DSWAPEFFECT_COPY; // D3DSWAPEFFECT_FLIP D3DSWAPEFFECT_DISCARD
    d3dpp.hDeviceWindow = hwndcanvas;
    d3dpp.Windowed = TRUE; // can not changed yet
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = MappingFormat( ctx->depthtype, ctx->depthprecision );
//  d3dpp.Flags = 0; // D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
    
    d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
//  d3dpp.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
    
    if (FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwndcanvas, VERTEXflag, &d3dpp, &g_pd3dDevice))){
        if (FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwndcanvas, VERTEXflag, &d3dpp, &g_pd3dDevice)))
            return Fail_CreateDevice;
    }
    numbackbuf = d3dpp.BackBufferCount;

    // if hardware render, the API.Present (BlockingMode) under WindowMode will pull down the speed greatly
    // TODO: call API.Present with async event 
    if (FAILED(g_pd3dDevice->GetRenderTarget( &g_pScreenTarget ))) // this will kept the first buffer 
        return Fail_GetRenderTarget;

    if (FAILED(g_pd3dDevice->GetDepthStencilSurface( &g_pDepthSurface ))) // this will incr first 
        return Fail_GetDepthStencilSurface;

    for ( UINT i=0; i<numbackbuf; ++i ) {
        IDirect3DSurface8* pBackBuffer;

        // get backbuffer : 
        if (FAILED(g_pd3dDevice->GetBackBuffer(i, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer)))
            return Fail_GetBackBuffer;

        // clear at once : 
        if (FAILED(g_pd3dDevice->SetRenderTarget(pBackBuffer, g_pDepthSurface)))
            return Fail_SetRenderTarget;

        // clear at once : 
        if (FAILED(g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, 0xFF000000, 1.0f, 0 )))
            return Fail_Clear;

        // release : 
        pBackBuffer->Release();
    }

    if (FAILED(g_pd3dDevice->SetRenderTarget( g_pScreenTarget, g_pDepthSurface ))) // reset to the first buffer 
        return Fail_SetRenderTarget;

    // init the enviorenment ... 
	if ( g_hint & HINT_CULLNONE )
		g_pd3dDevice->SetRenderState( D3DRS_CULLMODE,           D3DCULL_NONE );        // strip will take consider of the counter-clockwise or ... 
	else
		g_pd3dDevice->SetRenderState( D3DRS_CULLMODE,           D3DCULL_CCW );
	
	g_pd3dDevice->SetRenderState( D3DRS_LIGHTING,               FALSE );            // XYZ needs, XYZRHW no needs 
    g_pd3dDevice->SetRenderState( D3DRS_SHADEMODE,              D3DSHADE_FLAT );
    g_pd3dDevice->SetRenderState( D3DRS_LOCALVIEWER,            FALSE );
    g_pd3dDevice->SetRenderState( D3DRS_CLIPPLANEENABLE,        0x3F );                // 6 plane clipping is enabled 
    g_pd3dDevice->SetRenderState( D3DRS_MULTISAMPLEANTIALIAS,   FALSE );
    
    g_pd3dDevice->SetRenderState( D3DRS_COLORWRITEENABLE,       0x00000000 );        // bits: R(0)G(1)B(2)A(3)
    
    g_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE,        FALSE );
    g_pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC,              D3DCMP_GREATER );
    g_pd3dDevice->SetRenderState( D3DRS_ALPHAREF,               0x00000000 );
    
    g_pd3dDevice->SetRenderState( D3DRS_ZENABLE,                FALSE );
    g_pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE,           FALSE );
    g_pd3dDevice->SetRenderState( D3DRS_ZFUNC,                  D3DCMP_LESS );        // the less the better 
    
    g_pd3dDevice->SetRenderState( D3DRS_STENCILENABLE,          FALSE );
    g_pd3dDevice->SetRenderState( D3DRS_STENCILFUNC,            D3DCMP_GREATER );
    g_pd3dDevice->SetRenderState( D3DRS_STENCILREF,             0x00000000 );
    g_pd3dDevice->SetRenderState( D3DRS_STENCILPASS,            D3DSTENCILOP_INCR );

    // set the orgo-projection : just omit is OK !

    // SET texture stage state : 
    for ( int i=0; i<MAXARRAYCOUNT; ++i ) {
        g_pd3dDevice->SetTextureStageState( i, D3DTSS_ADDRESSU,		D3DTADDRESS_WRAP ); // D3DTADDRESS_BORDER 
        g_pd3dDevice->SetTextureStageState( i, D3DTSS_ADDRESSV,		D3DTADDRESS_WRAP );
        g_pd3dDevice->SetTextureStageState( i, D3DTSS_ADDRESSW,		D3DTADDRESS_WRAP );
		if ( g_hint & HINT_RENDER ) {
			g_pd3dDevice->SetTextureStageState( i, D3DTSS_MAGFILTER,	D3DTEXF_LINEAR );
			g_pd3dDevice->SetTextureStageState( i, D3DTSS_MINFILTER,	D3DTEXF_LINEAR );
		} else {
			g_pd3dDevice->SetTextureStageState( i, D3DTSS_MAGFILTER,	D3DTEXF_POINT );
			g_pd3dDevice->SetTextureStageState( i, D3DTSS_MINFILTER,	D3DTEXF_POINT );
		}
        g_pd3dDevice->SetTextureStageState( i, D3DTSS_MIPFILTER,	D3DTEXF_NONE  );
    }

    // clipping info : Ax + By + Cz + Dw < 0 will be clipped ! 
    float clipinfos[6][4] = { // w=1
        {  1.0f,  0.0f,  0.0f, 1.0f, }, // x < -1 : left
        { -1.0f,  0.0f,  0.0f, 1.0f, }, // x >  1 : right
        {  0.0f,  1.0f,  0.0f, 1.0f, }, // y < -1 : bottom 
        {  0.0f, -1.0f,  0.0f, 1.0f, }, // y >  1 : top
        {  0.0f,  0.0f,  1.0f, 0.0f, }, // z <  0 : front
        {  0.0f,  0.0f, -1.0f, 1.0f, }, // z >  1 : back
    };
    for ( int i=0; i<6; ++i ) 
        g_pd3dDevice->SetClipPlane( i, clipinfos[i] ); // front 

    // global state clear : 
    DX81Impl* p = (DX81Impl*)malloc( sizeof(DX81Impl) );
    ZeroMemory( p, sizeof(DX81Impl) );

    p->pD3D = g_pD3D;
    p->pd3dDevice = g_pd3dDevice;
    p->pScreenTarget = p->pRenderTarget = g_pScreenTarget;
    p->pDepthSurface = g_pDepthSurface;
	p->BackBufFormat = d3dpp.BackBufferFormat;
	p->vertexdecl = g_dwDecl;

    *hgpgpu = p;

    return (0);
}

int GpComputeSubCalc( HGPGPU hgpgpu, void* canvas, HGPGPUSUB* hgpgpusub )
{
	IDirect3DSwapChain8* pSwapChain;

	D3DPRESENT_PARAMETERS d3dpp; // settings of parameter : 
    ZeroMemory( &d3dpp, sizeof(d3dpp) );
    d3dpp.BackBufferFormat = hgpgpu->BackBufFormat;
    d3dpp.SwapEffect = D3DSWAPEFFECT_COPY; // D3DSWAPEFFECT_DISCARD
	d3dpp.hDeviceWindow = (HWND)canvas;
    d3dpp.Windowed = TRUE; // can not changed yet
	
	if (FAILED( hgpgpu->pd3dDevice->CreateAdditionalSwapChain(&d3dpp, &pSwapChain) ))
		return Fail_CreateSwapChain;

	// ok : 
	DX81Sub* p = (DX81Sub*)malloc( sizeof(DX81Sub) );
	p->pMain = hgpgpu;
	p->pSwapChain = pSwapChain;
	*hgpgpusub = p;

	return (0);
}

int GpComputeSubFree( HGPGPUSUB hgpgpusub )
{
	if (FAILED(hgpgpusub->pSwapChain->Release()))
		return Fail_Release;

	free( hgpgpusub );
	return (0);
}

int GpComputeFree( HGPGPU hgpgpu )
{
    if (FAILED(hgpgpu->pd3dDevice->SetIndices(NULL, 0)))
        return Fail_ClearIndices;

    for ( int i=0; i<MAXARRAYCOUNT; ++i ) {
        if (FAILED(hgpgpu->pd3dDevice->SetTexture(i, NULL)))
            return Fail_ClearTexture;
    }

    if (FAILED(hgpgpu->pScreenTarget->Release()))
        return Fail_Release;

    if (FAILED(hgpgpu->pDepthSurface->Release()))
        return Fail_Release;

	if (CHECK_RELEASE(hgpgpu->pd3dDevice->Release())) 
		return Fail_Release;

    if (CHECK_RELEASE(hgpgpu->pD3D->Release()))       
        return Fail_Release;

    free( hgpgpu );
    return (0);
}

int GpComputeMaxDimSize( HGPGPU /*hgpgpu*/, int dimid )
{
    if ( dimid==0 ) return MaxTextureWidth;
    if ( dimid==1 ) return MaxTextureHeight;
    return (0);
}

int GpComputeMaxShape( HGPGPU /*hgpgpu*/, int /*type*/ )
{
    return MaxPrimitiveCount / 2;
}

int GpComputeAllocate( HGPGPU hgpgpu, long width, long height, GP_TYPE t, GP_INPUTPREC f, GP_OPMODE p, HGPARRAY* hgparray )
{
    IDirect3DDevice8* g_pd3dDevice = hgpgpu->pd3dDevice;
    IDirect3DTexture8* pTexture;
    IDirect3DSurface8* pSurface;

    UINT pow2_width = pow2_round( width );
    UINT pow2_height = pow2_round( height );

    if (FAILED(g_pd3dDevice->CreateTexture( pow2_width, pow2_height, 1, MappingUsage(p), MappingFormat(t, f), D3DPOOL_SYSTEMMEM, &pTexture )))
        return Fail_CreateSysTexture;

    if (FAILED(pTexture->GetSurfaceLevel( 0, &pSurface )))
        return Fail_GetSurfaceLevel;

    if (FAILED(pTexture->Release()))
        return Fail_Release;

    D3DLOCKED_RECT lockinfo;
    if (FAILED(pSurface->LockRect( &lockinfo, NULL, D3DLOCK_NO_DIRTY_UPDATE|D3DLOCK_NOSYSLOCK )))
        return Fail_LockRect;
    
    DX81Array* rp = (DX81Array*)malloc(sizeof(DX81Array));
    rp->pSurface = pSurface;
    rp->lockinfo = lockinfo;
    *hgparray = rp; // assign ... 
    return (0);
}

int GpComputeDeallocate( HGPGPU /*hgpgpu*/, HGPARRAY hgparray )
{
    IDirect3DSurface8* pSurface = hgparray->pSurface;
    if (FAILED(pSurface->UnlockRect()))
        return Fail_UnlockRect;

    if (CHECK_RELEASE(pSurface->Release()))
        return Fail_Release;

    free( hgparray );
    return (0);
}

int    GpComputeGetPointer( HGPARRAY hgparray, void** pdata, long* pitch )
{
    *pdata = hgparray->lockinfo.pBits;
    *pitch = hgparray->lockinfo.Pitch;
    return (0);
}

int GpComputeAllocateVram( HGPGPU hgpgpu, long width, long height, GP_TYPE t, GP_INPUTPREC f, GP_OPMODE p, HGPVRRAY* gpvrray )
{
    IDirect3DDevice8* g_pd3dDevice = hgpgpu->pd3dDevice;
    IDirect3DTexture8* pTexture;

    UINT pow2_width = pow2_round( width );
    UINT pow2_height = pow2_round( height );

    if (FAILED(g_pd3dDevice->CreateTexture( pow2_width, pow2_height, 1, MappingUsage(p), MappingFormat(t, f), D3DPOOL_DEFAULT, &pTexture )))
        return Fail_CreateSysTexture;
    
    DX81Vrray* rp = (DX81Vrray*)malloc(sizeof(DX81Vrray));
    rp->pTexture = pTexture;
    *gpvrray = rp; // assign ... 
    return (0);
}

int GpComputeDeallocateVram( HGPGPU /*hgpgpu*/, HGPVRRAY hgpvrray )
{
    IDirect3DTexture8* pTexture = hgpvrray->pTexture;

    if (CHECK_RELEASE(pTexture->Release()))
        return Fail_Release;

    free( hgpvrray );
    return (0);
}

int GpComputeSubmitEx( HGPGPU hgpgpu, HGPARRAY hgparray, long xoff, long yoff, long xlen, long ylen, HGPVRRAY hgpvrray, long dstx, long dsty )
{
    IDirect3DDevice8* g_pd3dDevice = hgpgpu->pd3dDevice;
    IDirect3DSurface8* pSurface1 = hgparray->pSurface;
    IDirect3DTexture8* pTexture = hgpvrray->pTexture;
    IDirect3DSurface8* pSurface2;
    D3DSURFACE_DESC desc2;
    POINT pt = { dstx, dsty };

    if (FAILED(pTexture->GetSurfaceLevel( 0, &pSurface2 )))
        return Fail_GetSurfaceLevel;

    if (FAILED(pSurface2->GetDesc( &desc2 )))
        return Fail_GetDesc;

    // ------------------------- 
    if (FAILED(pSurface1->UnlockRect()))
        return Fail_UnlockRect;

    RECT rc;
    rc.left = xoff;
    rc.top = yoff;
    rc.right = rc.left + min(xlen, (LONG)desc2.Width-dstx);
    rc.bottom = rc.top + min(ylen, (LONG)desc2.Height-dsty);
    if (FAILED(g_pd3dDevice->CopyRects( pSurface1, &rc, 1, pSurface2, &pt )))
//  if (FAILED(g_pd3dDevice->CopyRects( pSurface1, NULL, 0, pSurface2, NULL )))
        return Fail_CopyRects;

    if (FAILED(pSurface2->Release()))
        return Fail_Release;

    if (FAILED(pSurface1->LockRect( &hgparray->lockinfo, NULL, D3DLOCK_NO_DIRTY_UPDATE|D3DLOCK_NOSYSLOCK )))
        return Fail_LockRect; // unprepare ...
    // ------------------------- 

    return (0);
}

int GpComputeSubmit( HGPGPU hgpgpu, HGPARRAY hgparray, HGPVRRAY hgpvrray, long dstx, long dsty )
{
    IDirect3DSurface8* pSurface1 = hgparray->pSurface;
    D3DSURFACE_DESC desc1;

    if (FAILED(pSurface1->GetDesc( &desc1 )))
        return Fail_GetDesc;

    return GpComputeSubmitEx( hgpgpu, hgparray, 0, 0, (LONG)desc1.Width, (LONG)desc1.Height, hgpvrray, dstx, dsty );
}

int GpComputeSelectSourceUnit( HGPGPU hgpgpu, HGPVRRAY hgpvrray, int index )
{
    if (0 > index || index >= MAXARRAYCOUNT)
        return Fail_InvalidSourceIndex;

    if ( hgpvrray ) {
        IDirect3DTexture8* pTexture = hgpvrray->pTexture;
        if (FAILED(hgpgpu->pd3dDevice->SetTexture( index, pTexture )))
            return Fail_SetTexture;
    } else {
        if (FAILED(hgpgpu->pd3dDevice->SetTexture( index, NULL )))
            return Fail_SetTexture;
    }

    return (0);
}

int GpComputeShapeRectTEx( HGPGPU hgpgpu, GP_RECT const* rects, GP_RECT const* trects, float const* depth, float const* div, int count, HGPSHAPERECT* vrect )
{
    IDirect3DDevice8* g_pd3dDevice = hgpgpu->pd3dDevice;
    IDirect3DVertexBuffer8* pVertexBuf;
    IDirect3DIndexBuffer8* pIndexBuf;
    VOID* ptr;

    if (FAILED(g_pd3dDevice->CreateVertexBuffer( 
        sizeof(VERTEX)*count*4, D3DUSAGE_WRITEONLY, 0/*D3DFVF_XYZRHW|D3DFVF_TEX0*/, D3DPOOL_DEFAULT, &pVertexBuf )))
        return Fail_CreateVertexBuffer;

    if (FAILED(pVertexBuf->Lock( 0, 0, (BYTE**)&ptr, 0 )))
        return Fail_VertexBufferLock;
    BuildVBFromMemRectT( ptr, rects, depth, div, trects, count );
    if (FAILED( pVertexBuf->Unlock() ))
        return Fail_VertexBufferUnlock;

    if (FAILED(g_pd3dDevice->CreateIndexBuffer( 
        sizeof(WORD)*count*6, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &pIndexBuf )))
        return Fail_CreateIndexBuffer;

    if (FAILED(pIndexBuf->Lock( 0, 0, (BYTE**)&ptr, 0 )))
        return Fail_IndexBufferLock;
    BuildIBFromMemRect( ptr, count );
    if (FAILED(pIndexBuf->Unlock()))
        return Fail_IndexBufferUnlock;

    DX81ShapeRect* p = (DX81ShapeRect*)malloc( sizeof(DX81ShapeRect) + (2*count-1) * sizeof(GP_RECT) );
    p->pVB = pVertexBuf;
    p->pIB = pIndexBuf;
    p->rectcount = count;
    memcpy( p->rects, rects, count * sizeof(GP_RECT) );
	if (trects) memcpy( p->rects+count, trects, count * sizeof(GP_RECT) );
    *vrect = p;

    return (0);
}

int GpComputeShapeRect( HGPGPU hgpgpu, GP_RECT const* rects, int count, HGPSHAPERECT* vrect )
{
    return GpComputeShapeRectTEx(hgpgpu, rects, NULL, NULL, NULL, count, vrect);
}
int GpComputeShapeRectEx( HGPGPU hgpgpu, GP_RECT const* rects, float const* depth, float const* div, int count, HGPSHAPERECT* vrect )
{
    return GpComputeShapeRectTEx(hgpgpu, rects, NULL, depth, div, count, vrect);
}
int GpComputeShapeRectT( HGPGPU hgpgpu, GP_RECT const* rects, GP_RECT const* trects, int count, HGPSHAPERECT* vrect )
{
    return GpComputeShapeRectTEx(hgpgpu, rects, trects, NULL, NULL, count, vrect);
}

int GpComputeShapeFree( HGPGPU /*hgpgpu*/, HGPSHAPERECT vertex )
{
    if (CHECK_RELEASE(vertex->pVB->Release()))
        return Fail_Release;

    if (CHECK_RELEASE(vertex->pIB->Release()))
        return Fail_Release;

    free( vertex );

    return (0);
}

int GpComputeSelectSourceShape( HGPGPU hgpgpu, HGPSHAPERECT shape )
{
    IDirect3DDevice8* g_pd3dDevice = hgpgpu->pd3dDevice;
    
    if ( shape ) {
        if (FAILED(g_pd3dDevice->SetStreamSource( 0, shape->pVB, sizeof(VERTEX)/*stride*/ )))
            return Fail_SetStreamSource;

        if (FAILED(g_pd3dDevice->SetIndices( shape->pIB, 0 )))
            return Fail_SetIndices;

        hgpgpu->beginidx = 0, hgpgpu->endidx = shape->rectcount;
    } else {
        if (FAILED(g_pd3dDevice->SetStreamSource( 0, NULL, sizeof(VERTEX)/*stride*/ )))
            return Fail_SetStreamSource;

        if (FAILED(g_pd3dDevice->SetIndices( NULL, 0 )))
            return Fail_SetIndices;

        hgpgpu->beginidx = hgpgpu->endidx = 0;
    }

    return (0);
}

int GpComputeBeginTarget( HGPGPU hgpgpu, HGPVRRAY hgpvrray, GP_PREPARE const* prepare )
{
    IDirect3DDevice8* g_pd3dDevice = hgpgpu->pd3dDevice;

    // 1. set render target 
    if ( hgpvrray ) {
        IDirect3DSurface8* pSurface;
        if (FAILED(hgpvrray->pTexture->GetSurfaceLevel( 0, &pSurface )))
            return Fail_GetSurfaceLevel;
        
        if (FAILED(g_pd3dDevice->SetRenderTarget( pSurface, hgpgpu->pDepthSurface )))
            return Fail_SetRenderTarget;
        
        if (FAILED(pSurface->Release()))
            return Fail_Release;

        hgpgpu->pRenderTarget = pSurface; // save non-backbuffer ptr 

    } else if ( hgpgpu->pScreenTarget != hgpgpu->pRenderTarget ) { // else means restore ... 
        // remove the original and restore the old one ! 
        if (FAILED(g_pd3dDevice->SetRenderTarget( hgpgpu->pScreenTarget, hgpgpu->pDepthSurface ))) 
            return Fail_SetRenderTarget;

        hgpgpu->pRenderTarget = hgpgpu->pScreenTarget; // save backbuffer ptr 
    }
    
    // 2. do clear :
    GP_PREPAREMASK fmask = prepare->flagmask;
    if ( fmask.depth_clear || fmask.color_clear || fmask.mask_clear ) {
        DWORD flags = 0;
        if ( fmask.color_clear ) flags |= D3DCLEAR_TARGET;
        if ( fmask.depth_clear ) flags |= D3DCLEAR_ZBUFFER;
        if ( fmask.mask_clear  ) flags |= D3DCLEAR_STENCIL;
        if (FAILED(g_pd3dDevice->Clear( 0, NULL, flags, prepare->value_color, prepare->value_depth, prepare->value_mask )))
            return Fail_Clear;
    }
    
    // 3. begin render : 
    if (FAILED(g_pd3dDevice->BeginScene()))
        return Fail_BeginScene;
    
    return (0);
}

int GpComputeEndTarget( HGPGPU hgpgpu, int present )
{
    IDirect3DDevice8* g_pd3dDevice = hgpgpu->pd3dDevice;
    if (FAILED(g_pd3dDevice->EndScene()))
        return Fail_EndScene;

    if (present) 
        return GpComputePresentOnCanvas( hgpgpu, NULL );

    return (0);
}

int GpComputePresentOnCanvas( HGPGPU hgpgpu, void* canvas )
{
    IDirect3DDevice8* g_pd3dDevice = hgpgpu->pd3dDevice;
	
//    hgpgpu->pScreenTarget->Release(); // kick off the prev one 

    if (FAILED(g_pd3dDevice->Present( NULL, NULL, (HWND)canvas, NULL )))
        return Fail_Present;

//    if (FAILED(g_pd3dDevice->GetRenderTarget( &hgpgpu->pScreenTarget ))) // request the new backbuf 
//        return Fail_GetRenderTarget;

//    char str[256];
//    wsprintfA( str, "0x%p\n", hgpgpu->pScreenTarget );
//    OutputDebugStringA( str );
	return (0);
}

int GpComputePresentSub( HGPGPUSUB hgpgpusub )
{
	IDirect3DSwapChain8* pSwapChain = hgpgpusub->pSwapChain;

	if (FAILED(pSwapChain->Present( NULL, NULL, NULL, NULL )))
		return Fail_PresentSwapChain;

	return (0);
}

int GpComputeSetProp( HGPGPU hgpgpu, GP_CALCSTATE const* calcstate )
{
    IDirect3DDevice8* g_pd3dDevice = hgpgpu->pd3dDevice;
    GP_CALCSTATE * p = &hgpgpu->currstate;
    if ( p->alpha_test != calcstate->alpha_test ) { // 
        BOOL newstate = !!calcstate->alpha_test;
        if (FAILED(g_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, newstate ))) return Fail_SetRenderState;
        if (calcstate->alpha_test > 0 && FAILED(g_pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATER) )) return Fail_SetRenderState; 
        if (calcstate->alpha_test < 0 && FAILED(g_pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_LESS) )) return Fail_SetRenderState; 
		p->alpha_test = calcstate->alpha_test;
    }
    if ( p->alpha_write    != calcstate->alpha_write ||
        p->red_write    != calcstate->red_write ||
        p->green_write  != calcstate->green_write ||
        p->blue_write   != calcstate->blue_write )
    {
        DWORD color = (calcstate->red_write<<0) | (calcstate->green_write<<1) | (calcstate->blue_write<<2) | (calcstate->alpha_write<<3);
        if (FAILED(g_pd3dDevice->SetRenderState( D3DRS_COLORWRITEENABLE, color ))) return Fail_SetRenderState;
        p->alpha_write    = calcstate->alpha_write;
        p->red_write    = calcstate->red_write;
        p->green_write  = calcstate->green_write;
        p->blue_write   = calcstate->blue_write;
    }
    if ( p->depth_test != calcstate->depth_test ) { // 
        if (FAILED(g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, calcstate->depth_test ))) return Fail_SetRenderState;
        if (calcstate->depth_test > 0 && FAILED(g_pd3dDevice->SetRenderState( D3DRS_ZFUNC, D3DCMP_LESS) )) return Fail_SetRenderState; 
        if (calcstate->depth_test < 0 && FAILED(g_pd3dDevice->SetRenderState( D3DRS_ZFUNC, D3DCMP_GREATER) )) return Fail_SetRenderState; 
        p->depth_test = calcstate->depth_test;
    }
    if ( p->depth_write != calcstate->depth_write ) { // 
        if (FAILED(g_pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, calcstate->depth_write ))) return Fail_SetRenderState;
        p->depth_write = calcstate->depth_write;
    }
    if ( p->mask_test != calcstate->mask_test ) { // 
        if (FAILED(g_pd3dDevice->SetRenderState( D3DRS_STENCILENABLE, calcstate->mask_test ))) return Fail_SetRenderState;
        p->mask_test = calcstate->mask_test;
    }
    return (0);
}

int GpComputeCalculateRect( HGPGPU hgpgpu, int beginidx, int endidx, GP_RECT const* /*dst*/ )
{
    IDirect3DDevice8* g_pd3dDevice = hgpgpu->pd3dDevice;

    if ( endidx <= beginidx ) {
        beginidx = hgpgpu->beginidx, endidx = hgpgpu->endidx;
    }

    // rectangle render : 
	if ( g_hint & HINT_WIRED ) {
		if (FAILED(g_pd3dDevice->DrawIndexedPrimitive(D3DPT_LINELIST, 
			beginidx*4, (endidx-beginidx)*4, beginidx*6, (endidx-beginidx)*3 )))
			return Fail_DrawPrimitive;
	} else {
		if (FAILED(g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 
			beginidx*4, (endidx-beginidx)*4, beginidx*6, (endidx-beginidx)*2 )))
			return Fail_DrawPrimitive;
	}

    return (0);
}

static unsigned long CreateVertexProgram( IDirect3DDevice8* g_pd3dDevice, const DWORD dwDecl[], const void* buffer )
{
    DWORD progid;
    if (FAILED(g_pd3dDevice->CreateVertexShader( dwDecl, (const DWORD*)buffer, &progid, VERTEXusage ))) 
        return 0xFFFFFFFF;

    return progid;
}

static unsigned long CreatePixelProgram( IDirect3DDevice8* g_pd3dDevice, const void* buffer )
{
    DWORD progid;
    if (FAILED(g_pd3dDevice->CreatePixelShader( (const DWORD*)buffer, &progid )))
        return 0xFFFFFFFF;

    return progid;
}

int GpComputeCreateProgram( HGPGPU hgpgpu, GP_PROGRAM t, unsigned long * progid, const void* buffer )
{
    IDirect3DDevice8* g_pd3dDevice = hgpgpu->pd3dDevice;
    switch( t ) {
    case PROGRAM_VERTEX:
		*progid = CreateVertexProgram( g_pd3dDevice, hgpgpu->vertexdecl, buffer ); break;
    case PROGRAM_PIXEL:
        *progid = CreatePixelProgram( g_pd3dDevice, buffer ); break;
    default:
        *progid = 0xFFFFFFFF; // error ... 
        return -1;
    }
    return (0);
}

int GpComputeDeleteProgram( HGPGPU hgpgpu, GP_PROGRAM t, unsigned long progid )
{
    IDirect3DDevice8* g_pd3dDevice = hgpgpu->pd3dDevice;
    switch( t ) {
    case PROGRAM_VERTEX:
        if (FAILED(g_pd3dDevice->DeleteVertexShader( progid )))
            return Fail_DeleteVertexShader;
        break;
    case PROGRAM_PIXEL:
        if (FAILED(g_pd3dDevice->DeletePixelShader( progid )))
            return Fail_DeletePixelShader;
        break;
    default:
        return -1;
    }
    return (0);
}

int GpComputeSelectProgram( HGPGPU hgpgpu, GP_PROGRAM t, unsigned long progid )
{
    IDirect3DDevice8* g_pd3dDevice = hgpgpu->pd3dDevice;
    switch( t ) {
    case PROGRAM_VERTEX:
        if (FAILED(g_pd3dDevice->SetVertexShader( progid )))
            return Fail_SetVertexShader;
        break;
    case PROGRAM_PIXEL:
        if (FAILED(g_pd3dDevice->SetPixelShader( progid )))
            return Fail_SetPixelShader;
        break;
    default:
        return -1;
    }
    return (0);
}

int GpComputeSetConstants( HGPGPU hgpgpu, GP_PROGRAM t, int offset, const void* buffer, int count )
{
    IDirect3DDevice8* g_pd3dDevice = hgpgpu->pd3dDevice;
    switch( t ) {
    case PROGRAM_VERTEX:
        if (FAILED(g_pd3dDevice->SetVertexShaderConstant( (DWORD)offset, buffer, (DWORD)count )))
            return Fail_SetVertexShaderConstant;
        break;
    case PROGRAM_PIXEL:
        if (FAILED(g_pd3dDevice->SetPixelShaderConstant( (DWORD)offset, buffer, (DWORD)count )))
            return Fail_SetPixelShaderConstant;
        break;
    default:
        return -1;
    }
    return (0);
}

int GpComputeSetDeclaration( HGPGPU hgpgpu, unsigned long * declare )
{
	hgpgpu->vertexdecl = ( NULL == declare ) ? g_dwDecl : declare;
	return (0);
}
